package com.zego.zegowawaji_server.device;

import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

import com.zego.base.log.PLogger;
import com.zego.base.utils.AppLogger;
import com.zego.zegowawaji_server.utils.JsonKey;

import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.util.Random;

/**
 *
 * 自己的娃娃机
 */
public class MyWawaji extends WawajiDevice {
    static final private int BAUD_RATE = 115200;

    static final private byte[] CMD_CHECK = {(byte) 0xFF, (byte) 0x55, (byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00
            , (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x14};

    // 初始化
    //static final private byte[] CMD_BYTE_BEGIN = {(byte) 0xFF, (byte) 0x55, (byte) 0xC1, (byte) 0x32, (byte) 0x46, (byte) 0x32, (byte) 0x14
    //        , (byte) 0x50, (byte) 0x64, (byte) 0x1E, (byte) 0x00, (byte) 0xA5};
    static final private byte[] CMD_BYTE_BEGIN = {(byte) 0xFF, (byte) 0x55, (byte) 0xC1, (byte) 0x46, (byte) 0x50, (byte) 0x46,
            (byte) 0x1E, (byte) 0x28, (byte) 0x46, (byte) 0x1E, (byte) 0x00, (byte) 0x9B};
    //static final private byte[] CMD_BYTE_BEGIN_GET = {(byte) 0xFF, (byte) 0x55, (byte) 0xC1, (byte) 0x3C, (byte) 0x3C, (byte) 0x32, (byte) 0x32
    //        , (byte) 0x32, (byte) 0x32, (byte) 0x1E, (byte) 0x00, (byte) 0x73};
    static final private byte[] CMD_BYTE_BEGIN_GET = {(byte) 0xFF, (byte) 0x55, (byte) 0xC1, (byte) 0x46, (byte) 0x50, (byte) 0x46,
            (byte) 0x46, (byte) 0x50, (byte) 0x64, (byte) 0x1E, (byte) 0x00, (byte) 0x09};
    // 操作
    static final private byte[] CMD_BYTE_FORWARD = {(byte) 0xFF, (byte) 0x55, (byte) 0xC2, (byte) 0xFB, (byte) 0x00, (byte) 0x00, (byte) 0x00
            , (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x12};
    static final private byte[] CMD_BYTE_BACKWARD = {(byte) 0xFF, (byte) 0x55, (byte) 0xC2, (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x00
            , (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x1C};
    static final private byte[] CMD_BYTE_LEFT = {(byte) 0xFF, (byte) 0x55, (byte) 0xC2, (byte) 0x00, (byte) 0xFB, (byte) 0x00, (byte) 0x00
            , (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x12};
    static final private byte[] CMD_BYTE_RIGHT = {(byte) 0xFF, (byte) 0x55, (byte) 0xC2, (byte) 0x00, (byte) 0x05, (byte) 0x00, (byte) 0x00
            , (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x1C};

    // 下抓
    static final private byte[] CMD_BYTE_DOWN = {(byte) 0xFF, (byte) 0x55, (byte) 0xC2, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00
            , (byte) 0x00, (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x1C};

    private Random mRandom = new Random();
    private DeviceStateListener mListener;
    // =================== 间隔进行设备查询 =============================
    private HandlerThread mMessageThread;
    private Handler mHandler;

    public MyWawaji(DeviceStateListener listener) throws SecurityException, IOException {
        // 私有，只能本应用使用，覆盖写入
        super(new File("/dev/ttyS1"), BAUD_RATE, Context.MODE_PRIVATE);
        mListener = listener;
        mRandom = new Random();

        Thread readThread = new ReadThread("my-reader");
        readThread.start();
    }

    @Override
    public boolean sendBeginCommand(JSONObject gameJson, int seq) {
        byte[] cmdData = CMD_BYTE_BEGIN;

        if (gameJson != null) {
            int catchPR = gameJson.optInt(JsonKey.KEY_CATCH_PR);

            if (catchPR == 100 || mRandom.nextInt(catchPR) == 1) {   //必中
/*
                int index = 6;
                cmdData[index++] = (byte) (mRandom.nextInt(8) + 60);    // 到顶爪力(1—100)
                cmdData[index++] = (byte) (mRandom.nextInt(8) + 60);    // 抓起爪力(1—100)
                cmdData[index++] = (byte) (mRandom.nextInt(8) + 60);    // 抓起高度(1—100)
                cmdData[index++] = (byte) (30); //游戏时间
                cmdData[index++] = (byte) (0);
                //计算校验和
                int sum = 0;
                for (int i = 0; i < cmdData.length - 1; i++) {
                    sum += cmdData[i];
                }
                cmdData[cmdData.length - 1] = (byte) (sum % 100); // 检验位
*/
                cmdData = CMD_BYTE_BEGIN_GET;
            } else {    //随机抓力抓取
                int catchUpRand = 8;
                int catchUpConst = 18;
                int catchTopRand = 5;
                int catchTopConst = 2;
                int catchMoveRand = 10;
                int catchMoveConst = 5;
                int catchBigRand = 10;
                int catchBigConst = 1;
                int catchHeightRand = 4;
                int catchHeightConst = 6;

                try {
                    Object upRand = gameJson.opt(JsonKey.KEY_CATCH_UP_RAND);
                    if (upRand instanceof String) {
                        catchUpRand = Integer.valueOf((String) upRand);
                    } else {
                        catchUpRand = (int) upRand;
                    }

                    Object upConst = gameJson.opt(JsonKey.KEY_CATCH_UP_CONST);
                    if (upConst instanceof String) {
                        catchUpConst = Integer.valueOf((String) upConst);
                    } else {
                        catchUpConst = (int) upConst;
                    }

                    Object topRand = gameJson.opt(JsonKey.KEY_CATCH_TOP_RAND);
                    if (topRand instanceof String) {
                        catchTopRand = Integer.valueOf((String) topRand);
                    } else {
                        catchTopRand = (int) topRand;
                    }

                    Object topConst = gameJson.opt(JsonKey.KEY_CATCH_TOP_CONST);
                    if (topConst instanceof String) {
                        catchTopConst = Integer.valueOf((String) topConst);
                    } else {
                        catchTopConst = (int) topConst;
                    }

                    Object moveRand = gameJson.opt(JsonKey.KEY_CATCH_MOVE_RAND);
                    if (moveRand instanceof String) {
                        catchMoveRand = Integer.valueOf((String) moveRand);
                    } else {
                        catchMoveRand = (int) moveRand;
                    }

                    Object moveConst = gameJson.opt(JsonKey.KEY_CATCH_MOVE_CONST);
                    if (moveConst instanceof String) {
                        catchMoveConst = Integer.valueOf((String) moveConst);
                    } else {
                        catchMoveConst = (int) moveConst;
                    }

                    Object bigRand = gameJson.opt(JsonKey.KEY_CATCH_BIG_RAND);
                    if (bigRand instanceof String) {
                        catchBigRand = Integer.valueOf((String) bigRand);
                    } else {
                        catchBigRand = (int) bigRand;
                    }

                    Object bigConst = gameJson.opt(JsonKey.KEY_CATCH_BIG_CONST);
                    if (bigConst instanceof String) {
                        catchBigConst = Integer.valueOf((String) bigConst);
                    } else {
                        catchBigConst = (int) bigConst;
                    }

                    Object heightRand = gameJson.opt(JsonKey.KEY_CATCH_HEIGHT_RAND);
                    if (heightRand instanceof String) {
                        catchHeightRand = Integer.valueOf((String) heightRand);
                    } else {
                        catchHeightRand = (int) heightRand;
                    }

                    Object heightConst = gameJson.opt(JsonKey.KEY_CATCH_HEIGHT_CONST);
                    if (heightConst instanceof String) {
                        catchHeightConst = Integer.valueOf((String) heightConst);
                    } else {
                        catchHeightConst = (int) heightConst;
                    }
                } catch (Exception e) {
                }


                int index = 6;
                cmdData[index++] = (byte) (mRandom.nextInt(catchTopRand) + catchTopConst);    // 到顶爪力(1—100)
                cmdData[index++] = (byte) (mRandom.nextInt(catchUpRand) + catchUpConst);    // 抓起爪力(1—100)
                cmdData[index++] = (byte) (mRandom.nextInt(catchHeightRand) + catchHeightConst);    // 抓起高度(1—100)
                cmdData[index++] = (byte) (30); //游戏时间
                cmdData[index++] = (byte) (0);

                //计算校验和
                int sum = 0;
                for (int i = 0; i < cmdData.length - 1; i++) {
                    sum += cmdData[i];
                }
                cmdData[cmdData.length-1] = (byte) sum;

            }
        } else {   // 数据拿失败，用默认随机
/*
            int index = 6;
            cmdData[index++] = (byte) (mRandom.nextInt(5) + 20);    // 到顶爪力(1—100)
            cmdData[index++] = (byte) (mRandom.nextInt(5) + 30);    // 抓起爪力(1—100)
            cmdData[index++] = (byte) (mRandom.nextInt(8) + 60);    // 抓起高度(1—100)
            cmdData[index++] = (byte) (30); //游戏时间
            cmdData[index++] = (byte) (0);

            //计算校验和
            int sum = 0;
            for (int i = 6; i < cmdData.length - 1; i++) {
                sum += (cmdData[i] & 0xff);
            }
            cmdData[cmdData.length - 1] = (byte) (sum % 100); // 检验位
        }
*/
            cmdData = CMD_BYTE_BEGIN;
        }
        updateSequence(cmdData, seq);

        return sendCommandData(cmdData);
    }


    @Override
    public boolean sendForwardCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_FORWARD, seq));
    }

    @Override
    public boolean sendBackwardCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_BACKWARD, seq));
    }

    @Override
    public boolean sendLeftCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_LEFT, seq));
    }

    @Override
    public boolean sendRightCommand(int seq) {
        return sendCommandData(updateSequence(CMD_BYTE_RIGHT, seq));
    }

    @Override
    public boolean sendGrabCommand(int seq) {
        initHandler();
        hasGrab = true;
        return sendCommandData(updateSequence(CMD_BYTE_DOWN, seq));
    }

    @Override
    public boolean checkDeviceState() {
        return false;
    }

    private byte[] updateSequence(byte[] data, int seq) {
//        data[1] = (byte) ((seq >> 8) & 0xff);
//        data[2] = (byte) (seq & 0xff);
//        data[4] = (byte) (~data[1] & 0xff);
//        data[5] = (byte) (~data[2] & 0xff);
        return data;
    }

    /**
     * 收到下位机的指令包
     */
    private void onResponseCommandReceived(byte[] data, int size) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(Integer.toHexString((data[i] & 0x000000FF) | 0xFFFFFF00).substring(6));
        }
        AppLogger.getInstance().writeLog("receive: %s from My device. data size: %d", sb.toString(), size);

        // 回馈用户游戏结果
        boolean win = ((data[4] & 0xff) != 0);
        if (mListener != null) {
            mListener.onGameOver(win);
        }
    }

    /**
     * 初始化并开启轮询
     */
    private void initHandler() {
        if (hasGrab) return;
        mMessageThread = new HandlerThread("command-check-timer");
        mMessageThread.start();

        mHandler = new Handler(mMessageThread.getLooper(), new HandlerImpl());
        mHandler.sendEmptyMessageDelayed(HandlerImpl.MSG_CHECK_DEVICE, 1000);
    }

    private class ReadThread extends Thread {
        private byte[] cmdBuffer = new byte[512];   // 读取的待分析数据
        private int bufferLength = 0;               // 读取的数据长度
        private int currentCmdLength = 6;           // 包长度固定为6

        public ReadThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            int size;
            byte[] buffer = new byte[64];

            while (!isInterrupted()) {
                if (mFileInputStream == null) break;

                try {
                    size = mFileInputStream.read(buffer);

                    for (int i = 0; i < size; i++) {
                        byte b = buffer[i];
                        if (bufferLength == 0 && b != (byte) 0xff) {  // 过滤掉不是以帧头FF开始数据
                            continue;
                        }
                        cmdBuffer[bufferLength++] = buffer[i];
                    }

                    // 打印下位机回馈数据指令
                    StringBuilder sb = new StringBuilder();
                    for (int i = 0; i < bufferLength; i++) {
                        sb.append(Integer.toHexString((cmdBuffer[i] & 0x000000FF) | 0xFFFFFF00).substring(6));
                    }
                    AppLogger.getInstance().writeLog("接收设备串口返回数据指令: size: %d,  %s", bufferLength, sb.toString());
                    PLogger.d("readBuffer:  size: " + size + "\n cmd: " + sb.toString());

                    // 指令示例： FF 55 C0 00 00 14
                    while (bufferLength >= 6) { // 帧头位 2 + 命令位 1 + 参数位 2 + 校验位 1
                        if ((cmdBuffer[0] == (byte) 0xff) &&        // 帧头位 1
                                (cmdBuffer[1] == (byte) 0x55)) {    // 帧头位 2

                            if (bufferLength >= currentCmdLength) {
                                // 查询操作，机子处于空闲状态且执行过抓取操作，才回馈用户
                                if ((cmdBuffer[2] == (byte) 0xC0) && cmdBuffer[3] == (byte) 0x00) {
                                    if (mHandler != null) {
                                        mHandler.removeMessages(HandlerImpl.MSG_CHECK_DEVICE);
                                        mHandler.sendEmptyMessageDelayed(HandlerImpl.MSG_CHECK_TO_SEND, 500);
                                    }
                                    if (!hasGrab) {
                                        boolean isValidate = checkCmdData(cmdBuffer, currentCmdLength);
                                        if (isValidate) {
                                            onResponseCommandReceived(cmdBuffer, currentCmdLength);
                                        }
                                        hasSend = true;
                                    }
                                    hasGrab = false;
                                }

//                                if ((cmdBuffer[2] == (byte) 0xC0) && cmdBuffer[3] == (byte) 0x00 && hasGrab) {
//                                    hasGrab = false;
//                                    boolean isValidate = checkCmdData(cmdBuffer, currentCmdLength);
//                                    if (isValidate) {
//                                        onResponseCommandReceived(cmdBuffer, currentCmdLength);
//                                    }
//                                }

                                int j = 0;
                                for (int i = currentCmdLength; i < bufferLength; i++) {
                                    cmdBuffer[j++] = cmdBuffer[i];
                                }
                                cmdBuffer[j] = '\0';
                                bufferLength = j;
                            } else {
                                break;
                            }
                        } else {    // 不合法，抛掉
                            int pos = -1;

                            StringBuilder tmpBuilder = new StringBuilder();
                            for (int i = 0; i < bufferLength; i++) {
                                byte b = cmdBuffer[i];
                                if (b == (byte) 0xfe) {
                                    pos = i;
                                    break;
                                }
                                tmpBuilder.append(Integer.toHexString((b & 0x000000FF) | 0xFFFFFF00).substring(6));
                            }
                            AppLogger.getInstance().writeLog("**** invalid data: %s *****", tmpBuilder.toString());

                            if (pos > 0) {
                                int j = 0;
                                for (int i = pos; i < bufferLength; i++) {
                                    cmdBuffer[j++] = cmdBuffer[i];
                                }
                                cmdBuffer[j] = '\0';
                                bufferLength = j;
                            } else {
                                cmdBuffer[0] = '\0';
                                bufferLength = 0;
                            }
                        }
                    }
                } catch (Exception e) {
                    AppLogger.getInstance().writeLog("DeviceManager's ReadThread Exception. e : %s", e);
                    break;
                }

                if (size < buffer.length) { // 如果不能填满缓冲区，则等待 500ms 后再读，否则立即读取下一段 buffer
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        AppLogger.getInstance().writeLog("DeviceManager's ReadThread wait Exception. e : ", e);
                    }
                }
            }
        }

        private boolean checkCmdData(byte[] data, int length) {
            byte sum = 0;
            for (int i = 0; i < length - 1; i++) {  // 最后的校验位不参与校验
                sum += data[i];
            }
            return sum == data[length - 1];
        }
    }

    private class HandlerImpl implements Handler.Callback {
        static final public int MSG_CHECK_DEVICE = 0x1; //  查询机器状态
        static final public int MSG_CHECK_TO_SEND = 0x2; //  查询后发送

        @Override
        public boolean handleMessage(Message message) {
            switch (message.what) {
                case MSG_CHECK_DEVICE: { // 查询设备状态轮询
                    if (hasGrab) {  // 执行过抓操作
                        sendCommandData(CMD_CHECK);
                        mHandler.sendEmptyMessageDelayed(MSG_CHECK_DEVICE, 1000);
                        return true;
                    }
                }
                break;

                case MSG_CHECK_TO_SEND:
                    if (!hasSend) {
                        sendCommandData(CMD_CHECK);
                        mHandler.sendEmptyMessageDelayed(MSG_CHECK_TO_SEND, 500);
                        return true;
                    }
                    hasSend = false;
                    mHandler.removeMessages(MSG_CHECK_TO_SEND);
                    break;
                default:
                    return false;
            }
            return true;
        }
    }
}
